using System;
using System.Collections.Generic;
using System.Data;
using System.Linq.Expressions;

namespace HF.Repository.MSSql
{
    public abstract class Repository<T> : ReadonlyRepository<T>
                                          , IRepository<T>, IRepository
        where T : RepositoryModel, new()
    {
        protected Repository(string tableName)
            : base(tableName)
        {
        }

        #region IRepository Members

        public void Create(RepositoryModel model)
        {
            Create((T)model);
        }

        public void Create(RepositoryModel model, IDbTransaction transaction)
        {
            Create((T)model, transaction);
        }

        public void Update(RepositoryModel model)
        {
            Update((T)model);
        }

        public void Update(RepositoryModel model, IDbTransaction transaction)
        {
            Update((T)model, transaction);
        }

        public void CreateOrUpdate(RepositoryModel model)
        {
            CreateOrUpdate((T)model);
        }

        public void CreateOrUpdate(RepositoryModel model, IDbTransaction transaction)
        {
            CreateOrUpdate((T)model, transaction);
        }

        public void Delete(RepositoryModel model)
        {
            Delete((T)model);
        }

        public void Delete(RepositoryModel model, IDbTransaction transaction)
        {
            Delete((T)model, transaction);
        }

        #endregion

        #region IRepository<T> Members

        public void Create(T model)
        {
            Create(model, null);
        }

        public void Create(T model, IDbTransaction transaction)
        {
            Create(model, transaction, true);
        }
        void Create(T model, IDbTransaction transaction, bool cache)
        {
            var sql = string.Format("insert into [{0}] {1} ", tableName,
                                    SqlRepositoryHelper.GetInsertString(model));
            ExecuteNonQuery(sqldb, transaction, sql);
            if (cache)
            {
                AddToCacheIfEnabled(model);
            }
        }


        public void Create(IEnumerable<T> list, IDbTransaction transaction)
        {
            foreach (var enumerable in list)
            {
                Create(enumerable, transaction, false);
            }
        }


        public void Update(T model)
        {
            Update(model, null);
        }

        public void Update(T model, IDbTransaction transaction)
        {
            Update(model, transaction, true);
        }
        void Update(T model, IDbTransaction transaction, bool cache)
        {
            T old = Get(model.GetKeyValue().ToString(), transaction);
            if (null == old)
            {
                return;
            }
            var sql = SqlRepositoryHelper.GetUpdateString(model, old);
            if (sql.Length == 0) return;
            sql = string.Format("update  [{0}] set {1} where  [{2}] = {3}", tableName, sql
                                , RepositoryReflectionCache.
                                      GetKeyName(typeof(T)),
                                model.GetKeyValue().ToSqlValue());

            ExecuteNonQuery(sqldb, transaction, sql);
            if (cache)
            {
                AddToCacheIfEnabled(model);
            }
        }


        public void Update(IEnumerable<T> list, IDbTransaction transaction)
        {
            foreach (var enumerable in list)
            {
                Update(enumerable, transaction, false);
            }
        }

        public void Update(Expression<Func<T, T>> action, Expression<Predicate<T>> where)
        {
            Update(action, where,null);
        }

        public void Update(Expression<Func<T, T>> action, Expression<Predicate<T>> where, IDbTransaction transaction)
        {

           // throw new NotImplementedException();

            var sqlaction = new SqlExpressionParser().GetSqlStatement(action);
            var sqlwhere = new SqlExpressionParser().GetSqlStatement(where);


            if (sqlaction.Length == 0) return;
            var sql = string.Format("update  [{0}] set {1} where 1=1 and  ({2}) ", tableName, sqlaction
                                    , sqlwhere);

            ExecuteNonQuery(sqldb, transaction, sql);


        }

        public void CreateOrUpdate(T model)
        {
            CreateOrUpdate(model, null);
        }

        public void CreateOrUpdate(T model, IDbTransaction transaction)
        {

            bool exist = Exists(model, transaction);
            if (exist)
                Update(model, transaction);
            else
                Create(model, transaction);
        }

        public virtual void Delete(T model)
        {
            Delete(model, null);
        }

        public void Delete(T model, IDbTransaction transaction)
        {
            var key = model.GetKeyValue();
            Delete(p => p.GetKeyValue() == key, transaction);
            RemoveFromCacheIfEnabled(key.ToString());
        }

        public void Delete(Expression<Predicate<T>> where)
        {
            Delete(where, null);
        }

        public void Delete(Expression<Predicate<T>> where, IDbTransaction transaction)
        {
            var sql = new SqlExpressionParser().GetSqlStatement(where);
            if (sql.Length == 0)
                return;
            sql = string.Format("delete from [{0}] {1} ", tableName,
                                string.Format(" where {0} ", sql));
            ExecuteNonQuery(sqldb, transaction, sql);
        }

        #endregion
    }
}